package com.tenton.service.Impl;

import com.alibaba.fastjson.JSON;
import com.mysql.cj.util.StringUtils;
import com.tenton.commons.OperateCommon;
import com.tenton.exception.DataNullPointerException;
import com.tenton.dao.StudentDao;
import com.tenton.exception.AgeFormatException;
import com.tenton.pojo.Student;
import com.tenton.service.StudentService;
import com.tenton.utils.ConstantUtil;
import com.tenton.utils.FileUtil;
import com.tenton.utils.MessageConstantUtil;
import com.tenton.utils.POIUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.*;

/**
 * @Date: 2021/2/2
 * @Author: Tenton
 * @Description:
 */
@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private OperateCommon operateCommon;
    /**
     * 增加学生
     * @param name
     * @param idCard
     * @param age
     * @param address
     * @param timestamp
     * @param session
     * @return
     */
    @Override
    public Map<String,String> insertStudent(String name, String idCard, int age, String address,Timestamp timestamp, HttpSession session) throws AgeFormatException, DataNullPointerException {
        String ageStr = String.valueOf(age);
        Map<String, String> map = operateCommon.addStudentCommons(name, idCard, ageStr, address, timestamp, session);
        return map;
    }

    /**
     * 批量插入学生
     * @param file
     * @param session
     * @return
     */
    @Override
    public Map<String,String> addStudent(MultipartFile file, HttpSession session) throws IOException, AgeFormatException, DataNullPointerException {
        Map<String,String> map = new HashMap();
        //获取当前时间
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        //读取上传的excel文件，解析成list集合
        List<String[]> list = POIUtil.readExcel(file);
        //判断excel文件中是否有数据
        if (CollectionUtils.isEmpty(list)) {
            map.put("code", MessageConstantUtil.EXCEL_NULL);
            throw new NullPointerException("数据为空");
        }else {
            //用于存储学生的姓名,身份证号码,年龄,地址字段
            List<Student> data = new ArrayList<>();
            //遍历,将对应的值赋值给Student对象,再存储到list集合中
            for (String[] strings : list) {
                //姓名  excel的第一列数据
                String name = strings[0];
                //身份证号码  excel的第二列数据
                String idCard = strings[1];
                //年龄  excel的第三列数据
                String ageStr = strings[2];
                //地址  excel的第四列数据
                String address = strings[3];
                map = operateCommon.addStudentCommons(name, idCard, ageStr, address, timestamp, session);
            }
        }
        return map;
    }

    /**
     * 模板下载
     * @param fileName
     */
    @Override
    public void downTemplate(String fileName) throws IOException {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletResponse response = requestAttributes.getResponse();
            // 设置信息给客户端不解析
            String type = new MimetypesFileTypeMap().getContentType(fileName);
            if (response != null) {
                // 设置content-type，即告诉客户端所发送的数据属于什么类型
                response.setHeader("Content-type", type);
                // 设置编码
                String code = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
                // 设置扩展头，当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
                response.setHeader("Content-Disposition", "attachment;filename=" + code);
                FileUtil.download(fileName, response);
            }
        }
    }

    /**
     * 删除学生
     * @param id
     * @return
     */
    @Override
    public Map<String,String> deleteStudent(Integer id) {
        Map<String,String> map = new HashMap();
        //对Id进行非空判断
        boolean idFlag = true;
        if (id.equals(null) || "".equals(id)){
            idFlag = false;
        }
        if (idFlag) {
            Student bystuId = studentDao.findBystuId(id);
            if (bystuId.getStudentStatus().equals(ConstantUtil.UNSTUDENTSTATUS)) {
                //获取redis中所有数据
                Set<String> studentInfo = redisTemplate.boundSetOps("studentInfo").members();
                //遍历
                for (String stu : studentInfo) {
                    //将JSON数据转换为对象
                    Student student = JSON.parseObject(stu, Student.class);
                    //如果获取的Id和redis中的数据Id一致
                    if (student.getId().equals(id)) {
                        //将此学生对象转换为JSON字符串
                        String stu1 = JSON.toJSONString(student);
                        //删除数据
                        redisTemplate.boundSetOps("studentInfo").remove(stu1);
                        //给前端传递一个删除成功的提示
                        map.put("code", MessageConstantUtil.DELETE_STUDENT_SUCCESS);
                        break;
                    }
                }
                //数据库删除数据
                studentDao.deleteById(id);
            }else {
                map.put("code",MessageConstantUtil.USE_STATUS);
            }
        }else {
            map.put("code",MessageConstantUtil.NUM_FORMAT_FAIT);
        }
        return map;
    }

    /**
     * 跳转到修改界面
     * @param id
     * @param model
     * @param session
     */
    @Override
    public void toUpdateStudent(Integer id, Model model,HttpSession session) {
        //获取session域中的管理员Id
        int adminId = (int) session.getAttribute("adminId");
        boolean idFlag = true;
        if (id.equals(null) && "".equals(id)){
            idFlag = false;
        }
        if (idFlag){
            //根据学生Id查询学生
            Student student = studentDao.findBystuId(id);
            //更改状态为正操作状态
            student.setStudentStatus(ConstantUtil.INSTUDENTSTATUS);
            //获取当前时间
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            //获取redis中所有数据
            Set<String> studentInfo = redisTemplate.boundSetOps("studentInfo").members();
            //遍历
            for (String stu : studentInfo) {
                //将JSON数据转换为对象
                Student studentRedis = JSON.parseObject(stu, Student.class);
                //将redis中的数据与查询出来的数据进行对比
                boolean compareStudents = operateCommon.compareStudents(student, studentRedis);
                //判断数据是否一致，不一致就修改
                if (compareStudents){
                    //先删除redis中已存在的数据
                    String stu1 = JSON.toJSONString(studentRedis);
                    redisTemplate.boundSetOps("studentInfo").remove(stu1);
                    //设置修改人Id
                    student.setUpdateId(adminId);
                    //设置修改时间
                    student.setUpdateTime(timestamp);
                    //将学生对象转换为JSON字符串,存入redis中
                    String newStu = JSON.toJSONString(student);
                    redisTemplate.boundSetOps("studentInfo").add(newStu);
                }
            }
            //更新数据库
            studentDao.save(student);
            //将学生信息存储到model域中，传递给前端
            model.addAttribute("student",student);
        }
    }

    /**
     * 修改学生
     * @param stuId
     * @param stuName
     * @param idCard
     * @param age
     * @param address
     * @param session
     * @return
     */
    @Override
    public Map<String,String> updateStudent(int stuId,String stuName,String idCard,int age,String address, HttpSession session) throws DataNullPointerException, AgeFormatException {
        //获取当前时间
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        String ageStr = String.valueOf(age);
        //修改学生数据
        return operateCommon.addStudentCommons(stuName, idCard, ageStr, address, timestamp, session);
    }
    /**
     * 获取所有学生
     * @return
     */
    @Override
    public List<Student> listStudent() {
        return studentDao.findAll();
    }
    /**
     * 通过学生编码查询学生（返回的是一个动态代理对象）
     * @param id
     * @return
     */
    @Override
    public Student getStudent(int id) {
        return studentDao.getOne(id);
    }

    /**
     * 根据学生Id查找学生（返回的是一个实体）
     * @param id
     * @return
     */
    @Override
    public Student findByStuId(int id) {
        return studentDao.findBystuId(id);
    }

    /**
     * 分页查询
     * @param pageNum
     * @param model
     * @return
     */
    @Override
    public void findAll(Integer pageNum, Model model) {
        //判断前端传递过来的pageNum是否为空
        if (pageNum == null){
            pageNum = 1;
        }
        //创建一个Pageable对象用于封装pageNUm和每页显示数据数量
        // （当前页， 每页记录数）
        Pageable pageable = PageRequest.of(pageNum - 1, ConstantUtil.PAGE_RECORD_NUM);
        //根据pageable对象查询当前登录用户的学生信息
        Page<Student> students = studentDao.findAll(pageable);
        model.addAttribute("students", students);
    }

    /**
     * 模糊查询
     * @param name
     * @param pageNum
     * @param model
     * @return
     */
    @Override
    public void pageQuery(String name, Integer pageNum,Model model) {
        //判断前端传递过来的pageNum是否为空
        if (pageNum == null){
            pageNum = 1;
        }
        Specification<Student> specification = new Specification<Student>() {
            @Override
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate predicate = null;
                //判断你是否输入了查询条件，如果有条件，就通过接口拼装sql进行模糊查询
                if (!StringUtils.isNullOrEmpty(name)) {
                    predicate = criteriaBuilder.like(root.get("name").as(String.class), "%" + name + "%");
                }
                return predicate;
            }
        };
        //当前页，分页条数
        Pageable pageable = PageRequest.of(pageNum - 1, ConstantUtil.PAGE_RECORD_NUM);
        Page<Student> list = studentDao.findAll(specification, pageable);
        //将学生信息添加到model域中，传递给前端
        model.addAttribute("studentList", list);
    }

    /**
     * 导出学生数据
     * @param response
     * @return
     */
    @Override
    public XSSFWorkbook exportData(HttpServletResponse response) {
        List<Student> students = studentDao.findAll();
        XSSFWorkbook wb = new XSSFWorkbook();
        //创建一张表
        Sheet sheet = wb.createSheet("Student");
        //创建第一行，起始为0
        Row titleRow = sheet.createRow(0);
        //第一列
        titleRow.createCell(0).setCellValue("序号");
        //第二列
        titleRow.createCell(1).setCellValue("学号");
        //第三列
        titleRow.createCell(2).setCellValue("姓名");
        //第四列
        titleRow.createCell(3).setCellValue("身份证");
        //第五列
        titleRow.createCell(4).setCellValue("年龄");
        //第六列
        titleRow.createCell(5).setCellValue("地址");
        //序号，默认为1
        int cell = 1;
        //遍历
        for (Student student : students) {
            //第一行保存的是每一列的列名
            //从第二行开始保存数据
            Row row = sheet.createRow(cell);
            //第一列 序号
            row.createCell(0).setCellValue(cell);
            //将数据库的数据遍历出来
            //第二列 学号
            row.createCell(1).setCellValue(student.getId());
            //第三列 姓名
            row.createCell(2).setCellValue(student.getName());
            //第四列 身份证
            row.createCell(3).setCellValue(student.getIdCard());
            //第五列 年龄
            row.createCell(4).setCellValue(student.getAge());
            //第六列 地址
            row.createCell(5).setCellValue(student.getAddress());
            //序号自增
            cell++;
        }
        //设置文档名称，这儿写死了，也可以前端传输（前端传一个文件名到后端就行）
        String fileName = "学生信息表.xlsx";
        OutputStream outputStream =null;
        try {
            //文件名编码格式
            fileName = URLEncoder.encode(fileName,"UTF-8");
            //设置ContentType请求信息格式
            response.setContentType("application/vnd.ms-excel");
            //设置标头
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            outputStream = response.getOutputStream();
            wb.write(outputStream);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return wb;
    }

    /**
     * 跳转到主页  更改数据的状态码
     * @param id
     * @return
     */
    @Override
    public Map<String, String> retMain(Integer id) {
        Map<String,String> map = new HashMap();
        //id非空判断
        boolean idFlag = true;
        if (id.equals(null) || "".equals(id)){
            idFlag = false;
        }
        if (idFlag){
            //根据id查询学生
            Student student = studentDao.findBystuId(id);
            //更改数据状态
            student.setStudentStatus(ConstantUtil.UNSTUDENTSTATUS);
            //保存学生数据
            studentDao.save(student);
            //给前端传递一个成功的提示
            map.put("code",MessageConstantUtil.RET_MAIN_SUCCESS);
        }
        return map;
    }
}
